﻿using System;
using System.IO;
using System.Web;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using CookComputing.XmlRpc;

using umbraco;
using umbraco.BusinessLogic;
using umbraco.presentation.channels.businesslogic;
using umbraco.cms.businesslogic.web;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.propertytype;
using umbraco.cms.businesslogic.datatype;
using umbraco.cms.businesslogic.media;
using umbraco.cms.businesslogic.property;
using umbraco.interfaces;


namespace tswe.channels
{
    /// <summary>
    /// the umbraco channels API is xml-rpc webservice based on the metaweblog and blogger APIs
    /// for editing umbraco data from external clients
    /// </summary>
    [XmlRpcService(
        Name = "TSWE extended metablog api for umbraco",
        Description = "for editing umbraco data from external client blog writer applications",
        AutoDocumentation = true)]
    [XmlRpcMissingMapping(MappingAction.Ignore)]
    public class MetaWeblog : XmlRpcService, IMetaWeblog
    {
        #region Public Constructors

        public MetaWeblog()
        {
        }

        #endregion

        #region IMetaWeblog Members

        string IMetaWeblog.NewPost(string blogid, string username, string password,
            Post post, bool publish)
        {
            ValidateUser(username, password);

            Channel userChannel = new Channel(username);
            User u = new User(username);
            Document doc =
                Document.MakeNew(HttpContext.Current.Server.HtmlDecode(post.title),
                                 DocumentType.GetByAlias(userChannel.DocumentTypeAlias), u,
                                 userChannel.StartNode);

            // Excerpt
            if (!String.IsNullOrEmpty(userChannel.FieldExcerptAlias))
                doc.getProperty(userChannel.FieldExcerptAlias).Value 
                    = removeLeftUrl(post.mt_excerpt);

            // Description
            if (UmbracoSettings.TidyEditorContent)
                doc.getProperty(userChannel.FieldDescriptionAlias).Value 
                    = library.Tidy(removeLeftUrl(post.description), false);
            else
                doc.getProperty(userChannel.FieldDescriptionAlias).Value 
                    = removeLeftUrl(post.description);
            
            // Categories
            updateCategories(doc, post, userChannel);

            if (publish)
            {
                doc.Publish(new User(username));
                library.UpdateDocumentCache(doc.Id);
            }
            return doc.Id.ToString();
        }

        bool IMetaWeblog.UpdatePost(string postid, string username, string password,
            Post post, bool publish)
        {
            ValidateUser(username, password);
            Channel userChannel = new Channel(username);

            Document doc = getDocumentById(postid);
            if (doc == null) return true;
            
            doc.Text = HttpContext.Current.Server.HtmlDecode(post.title);
            if (post.date_created_gmt != null)
                doc.CreateDateTime = (DateTime)post.date_created_gmt;

            // Excerpt
            if (!String.IsNullOrEmpty(userChannel.FieldExcerptAlias))
                doc.getProperty(userChannel.FieldExcerptAlias).Value 
                    = removeLeftUrl(post.mt_excerpt);

            if (UmbracoSettings.TidyEditorContent)
                doc.getProperty(userChannel.FieldDescriptionAlias).Value = 
                    library.Tidy(removeLeftUrl(post.description), false);
            else
                doc.getProperty(userChannel.FieldDescriptionAlias).Value 
                    = removeLeftUrl(post.description);
            
            updateCategories(doc, post, userChannel);

            if (publish)
            {
                doc.Publish(new User(username));
                library.UpdateDocumentCache(doc.Id);
            }
            else
            {
                doc.UnPublish();
                library.UnPublishSingleNode(doc.Id);
            }
            return true;
        }

        Post IMetaWeblog.GetPost(string postid, string username, string password)
        {
            ValidateUser(username, password);
            Channel userChannel = new Channel(username);
            Document d = getDocumentById(postid);
            if (d == null) return new Post();
            
            Post p = new Post();
            p.title = d.Text;
            p.description = d.getProperty(userChannel.FieldDescriptionAlias).Value.ToString();

            // Excerpt
            if (userChannel.FieldExcerptAlias != null && userChannel.FieldExcerptAlias != "")
                p.mt_excerpt = d.getProperty(userChannel.FieldExcerptAlias).Value.ToString();

            // Categories
            if (userChannel.FieldCategoriesAlias != null && userChannel.FieldCategoriesAlias != "" &&
                d.getProperty(userChannel.FieldCategoriesAlias) != null &&
                d.getProperty(userChannel.FieldCategoriesAlias).Value != null &&
                d.getProperty(userChannel.FieldCategoriesAlias).Value.ToString() != "")
            {
                String categories = d.getProperty(userChannel.FieldCategoriesAlias).Value.ToString();
                char[] splitter = { ',' };
                String[] categoryIds = categories.Split(splitter);
                p.categories = getCategoryValues(categoryIds, userChannel); 
            }

            p.postid = postid;
            p.permalink = library.NiceUrlFullPath(d.Id); // library.NiceUrl(d.Id);
            p.dateCreated = d.CreateDateTime;
            p.date_created_gmt = d.CreateDateTime;
            p.link = p.permalink;
            //if (d.Published)
            //    p.post_status = "publish";
            //else
            //    p.post_status = "draft";

            return p;
        }

        private static string[] getCategoryValues(String[] categoryIds, Channel userChannel)
        {
            // Find the propertytype via the document type
            ContentType blogPostType = ContentType.GetByAlias(userChannel.DocumentTypeAlias);
            PropertyType categoryType = blogPostType.getPropertyType(userChannel.FieldCategoriesAlias);
            SortedList categoriesList = PreValues.GetPreValues(categoryType.DataTypeDefinition.Id);

            List<string> values = new List<string>();
            foreach (string categoryId in categoryIds)
                foreach (PreValue category in categoriesList.Values)
                    if (category.Id == Int32.Parse(categoryId))
                        values.Add(category.Value);
            return values.ToArray();
        }

        CategoryInfo[] IMetaWeblog.GetCategories(string blogid, string username, string password)
        {
            ValidateUser(username, password);

            Channel userChannel = new Channel(username);
            if (String.IsNullOrEmpty(userChannel.FieldCategoriesAlias))
                throw new XmlRpcFaultException(0, "Categories doesn't work for this channel, "
                    + "they might not have been activated. Contact your umbraco administrator.");

            // Find the propertytype via the document type
            ContentType blogPostType = ContentType.GetByAlias(userChannel.DocumentTypeAlias);
            PropertyType categoryType = blogPostType.getPropertyType(userChannel.FieldCategoriesAlias);

            // check if the datatype uses tags or prevalues
            CategoryInfo[] returnedCategories = null;
            IUseTags tags = UseTags(categoryType);
            if (tags != null)
            {
                List<ITag> alltags = tags.GetAllTags();
                returnedCategories = new CategoryInfo[alltags.Count];
                int counter = 0;
                foreach (ITag t in alltags)
                {
                    CategoryInfo ci = new CategoryInfo();
                    ci.title = t.TagCaption;
                    ci.categoryid = t.Id.ToString();
                    ci.description = "";
                    ci.rssUrl = "";
                    ci.htmlUrl = "";
                    returnedCategories[counter] = ci;
                    counter++;
                }
            }
            else
            {
                SortedList categories = PreValues.GetPreValues(categoryType.DataTypeDefinition.Id);
                returnedCategories = new CategoryInfo[categories.Count];
                IDictionaryEnumerator ide = categories.GetEnumerator();
                int counter = 0;
                while (ide.MoveNext())
                {
                    PreValue category = (PreValue)ide.Value;
                    CategoryInfo ci = new CategoryInfo();
                    ci.title = category.Value;
                    ci.categoryid = category.Id.ToString();
                    ci.description = "";
                    ci.rssUrl = "";
                    ci.htmlUrl = "";
                    returnedCategories[counter] = ci;
                    counter++;
                }
            }

            return returnedCategories;
        }

        public static IUseTags UseTags(PropertyType categoryType)
        {
            if (typeof(IUseTags).IsAssignableFrom(
                categoryType.DataTypeDefinition.DataType.DataEditor.GetType()))
            {
                IUseTags tags = (IUseTags)categoryType.DataTypeDefinition.DataType.DataEditor 
                    as IUseTags;
                return tags;
            }
            return null;
        }

        /// <summary>
        /// Get all existing categories
        /// </summary>
        /// <param name="blogid">The blogid.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        wpCategory[] IMetaWeblog.getWpCategories(string blogid, string username, string password/*, object dummyCategory*/)
        {
            ValidateUser(username, password);
            Channel userChannel = new Channel(username);
            if (userChannel.FieldCategoriesAlias != null && userChannel.FieldCategoriesAlias != "")
            {
                // Find the propertytype via the document type
                ContentType blogPostType = ContentType.GetByAlias(userChannel.DocumentTypeAlias);
                PropertyType categoryType = blogPostType.getPropertyType(userChannel.FieldCategoriesAlias);

                SortedList categories = PreValues.GetPreValues(categoryType.DataTypeDefinition.Id);
                wpCategory[] returnedCategories = new wpCategory[categories.Count];
                IDictionaryEnumerator ide = categories.GetEnumerator();
                int counter = 0;
                while (ide.MoveNext())
                {
                    PreValue category = (PreValue)ide.Value;
                    wpCategory ci = new wpCategory();
                    ci.categoryName = category.Value;
                    ci.categoryId = category.Id;
                    ci.description = "";
                    ci.parentId = -1;
                    ci.rssUrl = "";
                    ci.htmlUrl = "";
                    returnedCategories[counter] = ci;
                    counter++;
                }
                return returnedCategories;
            }
            throw new ArgumentException("Categories doesn't work for this channel. Contact your umbraco administrator.");
        }

        /// <summary>
        /// Creates a new blog category / tag.
        /// </summary>
        /// <param name="blogid">The blogid.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <param name="category">The category.</param>
        /// <returns></returns>
        string IMetaWeblog.newCategory(string blogid, string username, string password,
            wpCategory category)
        {
            int result = 0;
            ValidateUser(username, password);
            Channel userChannel = new Channel(username);
            if (!String.IsNullOrEmpty(userChannel.FieldCategoriesAlias))
            {
                // Find the propertytype via the document type
                ContentType blogPostType = ContentType.GetByAlias(userChannel.DocumentTypeAlias);
                PropertyType categoryType = blogPostType.getPropertyType(userChannel.FieldCategoriesAlias);

                IUseTags tags = UseTags(categoryType);
                if (tags != null)
                {
                    tags.AddTag(category.name);
                }
                else
                {
                    PreValue pv = new PreValue();
                    pv.DataTypeId = categoryType.DataTypeDefinition.Id;

                    // iwache: hack for iPhone WordPress 1.1
                    // if no category.categoryName available then use category.name
                    if (String.IsNullOrEmpty(category.categoryName))
                        category.categoryName = category.name;

                    pv.Value = category.categoryName;
                    pv.Save();
                    result = pv.Id;
                }
            }
            return result.ToString();
        }

        /// <summary>
        /// Deletes a existing category / tag.
        /// </summary>
        /// <param name="blogid">The blogid.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <param name="category">The category.</param>
        /// <param name="categoryid">The category id.</param>
        /// <returns></returns>
        string IMetaWeblog.deleteCategory(
            string blogid,
            string username,
            string password,
            string categoryid)
        {
            ValidateUser(username, password);
            /*
            Channel userChannel = new Channel(username);
            if (userChannel.FieldCategoriesAlias != null && userChannel.FieldCategoriesAlias != "")
            {
                // Find the propertytype via the document type
                ContentType blogPostType = ContentType.GetByAlias(userChannel.DocumentTypeAlias);
                PropertyType categoryType = blogPostType.getPropertyType(userChannel.FieldCategoriesAlias);

                PreValue pv = new PreValue();
                pv.DataTypeId = categoryType.DataTypeDefinition.Id;
                pv.Value = category.name;
                pv.Save();
            }
            */
            return String.Empty;
        }

        /// <summary>
        /// Get all existing blog authors
        /// </summary>
        /// <param name="blogid">The blogid.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        wpAuthor[] IMetaWeblog.getAuthors(
            string blogid,
            string username,
            string password)
        {
            ValidateUser(username, password);
            User u = new User(username);
            wpAuthor[] authors = new wpAuthor[1];
            wpAuthor author = new wpAuthor();
            author.user_id = u.Id;
            author.user_login = u.LoginName;
            author.display_name = u.Name;
            author.user_email = u.Email;
            author.meta_value = "";
            authors[0] = author;
            return authors;
        }

        XmlRpcStruct IMetaWeblog.getPostStatusList(
             string blogid,
             string username,
             string password)
        {
            ValidateUser(username, password);
            XmlRpcStruct statusList = new XmlRpcStruct();
            statusList.Add("draft", "Draft");
            //statusList.Add("pending", "Pending Review");
            //statusList.Add("private", "Private");
            statusList.Add("publish", "Published");
            return statusList;
        }

        Post[] IMetaWeblog.GetRecentPosts(string blogid, string username, string password,
            int numberOfPosts)
        {
            ValidateUser(username, password);

            ArrayList blogPosts = new ArrayList();
            ArrayList blogPostsObjects = new ArrayList();
            User u = new User(username);
            Channel userChannel = new Channel(u.Id);
            Document rootDoc;
            if (userChannel.StartNode > 0)
                rootDoc = new Document(userChannel.StartNode);
            else
            {
                if (u.StartNodeId == -1)
                {
                    rootDoc = Document.GetRootDocuments()[0];
                }
                else
                {
                    rootDoc = new Document(u.StartNodeId);
                }
            }

            foreach (Document d in rootDoc.Children)
            {
                int count = 0;
                blogPosts.AddRange(
                    findBlogPosts(userChannel, d, u.Name, ref count, numberOfPosts, userChannel.FullTree));
            }
            blogPosts.Sort(new DocumentSortOrderComparer());

            foreach (Object o in blogPosts)
            {
                Document d = (Document)o;
                Post p = new Post();
                p.dateCreated = d.CreateDateTime;
                p.date_created_gmt = d.CreateDateTime;
                p.userid = username;
                p.title = d.Text;
                p.permalink = library.NiceUrlFullPath(d.Id); // library.NiceUrl(d.Id);
                p.link = p.permalink;
                p.description = d.getProperty(userChannel.FieldDescriptionAlias).Value.ToString();
                p.postid = d.Id.ToString();

                //if (d.Published)
                //    p.post_status = "publish";
                //else
                //    p.post_status = "draft";

                if (!String.IsNullOrEmpty(userChannel.FieldCategoriesAlias) &&
                    d.getProperty(userChannel.FieldCategoriesAlias) != null &&
                    d.getProperty(userChannel.FieldCategoriesAlias).Value != null &&
                    d.getProperty(userChannel.FieldCategoriesAlias).Value.ToString() != "")
                {
                    String categories = d.getProperty(userChannel.FieldCategoriesAlias).Value.ToString();
                    char[] splitter = { ',' };
                    String[] categoryIds = categories.Split(splitter);

                    // Find the propertytype via the document type
                    ContentType blogPostType = ContentType.GetByAlias(userChannel.DocumentTypeAlias);
                    PropertyType categoryType = blogPostType.getPropertyType(userChannel.FieldCategoriesAlias);

                    // check if the datatype uses tags or prevalues
                    IUseTags tags = UseTags(categoryType);
                    if (tags == null)
                        p.categories = getCategoryValues(categoryIds, userChannel);
                    else
                        p.categories = categoryIds;
                }

                // Excerpt
                if (!String.IsNullOrEmpty(userChannel.FieldExcerptAlias))
                    p.mt_excerpt = d.getProperty(userChannel.FieldExcerptAlias).Value.ToString();
                //p.mt_excerpt = "This is an Excerpt";


                //// added for iBlogger
                //p.mt_allow_comments = true;
                //p.mt_allow_pings = true;
                //p.mt_keywords = "Key-1, Key-2, Key-3";
                //p.wp_slug = p.title;
                //p.wp_author_display_name = u.Name;


                blogPostsObjects.Add(p);
            }
            return (Post[])blogPostsObjects.ToArray(typeof(Post));
        }

        MediaObjectInfo IMetaWeblog.NewMediaObject(string blogid, string username, string password,
            MediaObject mediaObject)
        {
            ValidateUser(username, password);
            return newMediaObjectLogic(blogid, username, password, mediaObject);
        }

        protected MediaObjectInfo newMediaObjectLogic(string blogid, string username, string password,
            MediaObject file)
        {
            User u = new User(username);
            Channel userChannel = new Channel(username);
            MediaObjectInfo fileUrl = new MediaObjectInfo();
            if (userChannel.ImageSupport)
            {
                Media rootNode;
                if (userChannel.MediaFolder > 0)
                    rootNode = new Media(userChannel.MediaFolder);
                else
                    rootNode = new Media(u.StartMediaId);

                // Create new media
                Media m = Media.MakeNew(file.name, MediaType.GetByAlias(userChannel.MediaTypeAlias), 
                    u, rootNode.Id);

                Property fileObject = m.getProperty(userChannel.MediaTypeFileProperty);
                string _fullFilePath;
                string filename = file.name.Replace("/", "_");
                // Generate file
                if (UmbracoSettings.UploadAllowDirectories)
                {
                    // Create a new folder in the /media folder with the name /media/propertyid
                    Directory.CreateDirectory(
                        HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../media/" + fileObject.Id));
                    _fullFilePath =
                        HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../media/" + fileObject.Id +
                                                           "/" + filename);
                    fileObject.Value = "/media/" + fileObject.Id + "/" + filename;
                }
                else
                {
                    filename = fileObject.Id + "-" + filename;
                    _fullFilePath =
                        HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../media/" + filename);
                    fileObject.Value = "/media/" + filename;
                }
                fileUrl.url = "http://" + HttpContext.Current.Request.ServerVariables["SERVER_NAME"] +
                              fileObject.Value.ToString();

                File.WriteAllBytes(_fullFilePath, file.bits);

                // Try updating standard file values
                try
                {
                    string orgExt = "";
                    // Size
                    if (m.getProperty("umbracoBytes") != null)
                        m.getProperty("umbracoBytes").Value = file.bits.Length;
                    // Extension
                    if (m.getProperty("umbracoExtension") != null)
                    {
                        orgExt =
                            ((string)
                             file.name.Substring(file.name.LastIndexOf(".") + 1,
                                                 file.name.Length - file.name.LastIndexOf(".") - 1));
                        m.getProperty("umbracoExtension").Value = orgExt.ToLower();
                    }
                    // Width and Height
                    // Check if image and then get sizes, make thumb and update database
                    if (m.getProperty("umbracoWidth") != null && m.getProperty("umbracoHeight") != null &&
                        ",jpeg,jpg,gif,bmp,png,tiff,tif,".IndexOf("," + orgExt.ToLower() + ",") > 0)
                    {
                        int fileWidth;
                        int fileHeight;

                        FileStream fs = new FileStream(_fullFilePath,
                                                       FileMode.Open, FileAccess.Read, FileShare.Read);

                        Image image = Image.FromStream(fs);
                        fileWidth = image.Width;
                        fileHeight = image.Height;
                        fs.Close();
                        try
                        {
                            m.getProperty("umbracoWidth").Value = fileWidth.ToString();
                            m.getProperty("umbracoHeight").Value = fileHeight.ToString();
                        }
                        catch { }
                    }
                }
                catch { }
                return fileUrl;
            }
            else throw new XmlRpcFaultException(0, "Image Support is turned off in this channel. "
                + "Modify channel settings in umbraco to enable image support.");
        }

        private bool deletePost(string key, string postid, string username, string password, bool publish)
        {
            ValidateUser(username, password);
            Channel userChannel = new Channel(username);
            Document doc = getDocumentById(postid);
            if (doc != null)
            {
                doc.delete();
                library.UnPublishSingleNode(int.Parse(postid));
            }
            return true;
        }

        private Document getDocumentById(string sId)
        {
            int id;
            if (int.TryParse(sId, out id))
                try
                {
                    return new Document(id);
                }
                catch (Exception e)
                {
                    return null;
                }
            else return null;
        }

        bool IMetaWeblog.DeletePost(string key, string postid, string username, string password, bool publish)
        {
            return deletePost(key, postid, username, password, publish);
        }

        bool IMetaWeblog.BloggerDeletePost(string key, string postid, string username, string password, bool publish)
        {
            return deletePost(key, postid, username, password, publish);
        }

        BlogInfo[] IMetaWeblog.GetUsersBlogs(string key, string username, string password)
        {
            ValidateUser(username, password);
            BlogInfo[] blogs = new BlogInfo[1];
            User user = new User(username);
            Channel userChannel = new Channel(user.Id);
            Document rootDoc;
            if (userChannel.StartNode > 0)
                rootDoc = new Document(userChannel.StartNode);
            else
                rootDoc = new Document(user.StartNodeId);
            BlogInfo bInfo = new BlogInfo();
            bInfo.blogName = userChannel.Name;
            bInfo.blogid = rootDoc.Id.ToString();
            bInfo.url = getBlogUrl(user);
            blogs[0] = bInfo;
            return blogs;
        }

        private string getBlogUrl(User user)
        {
            Channel channel = new Channel(user.Id);
            Document rootDoc;
            if (channel.StartNode > 0)
                rootDoc = new Document(channel.StartNode);
            else
                rootDoc = new Document(user.StartNodeId);

            string url = library.NiceUrl(rootDoc.Id);
            if (!url.StartsWith("http://"))
                url = "http://" + HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + url;

            return url;
        }


        UserInfo IMetaWeblog.GetUserInfo(string key, string username, string password)
        {
            ValidateUser(username, password);
            UserInfo userInfo = new UserInfo();
            User user = new User(username);
            Channel userChannel = new Channel(user.Id);

            userInfo.userid = user.Id.ToString();
            userInfo.url = getBlogUrl(user);
            userInfo.email = user.Email;
            userInfo.nickname = user.LoginName;
            userInfo.lastname = user.Name;
            userInfo.firstname = "";
            return userInfo;
        }

        #endregion

        #region Private Methods

        private bool ValidateUser(string username, string password)
        {
            if (!User.validateCredentials(username, password, false))
                throw new XmlRpcFaultException(0, "Invalid user or password.");
            return true;
        }

        private void updateCategories(Document doc, Post post, Channel userChannel)
        {
            if (!String.IsNullOrEmpty(userChannel.FieldCategoriesAlias))
            {
                ContentType blogPostType = ContentType.GetByAlias(userChannel.DocumentTypeAlias);
                PropertyType categoryType = blogPostType.getPropertyType(userChannel.FieldCategoriesAlias);

                String[] categories = post.categories;
                string categoryValue = "";

                IUseTags tags = UseTags(categoryType);
                if (tags != null)
                {
                    tags.RemoveTagsFromNode(doc.Id);
                    if (categories != null)
                        for (int i = 0; i < categories.Length; i++)
                            tags.AddTagToNode(doc.Id, categories[i]);
                }
                else
                {
                    if (categories != null)
                        for (int i = 0; i < categories.Length; i++)
                        {
                            PreValue pv = new PreValue(categoryType.DataTypeDefinition.Id, categories[i]);
                            categoryValue += pv.Id + ",";
                        }
                    categoryValue.TrimEnd(',');
                    doc.getProperty(userChannel.FieldCategoriesAlias).Value = categoryValue;
                }
            }
        }

        protected ArrayList findBlogPosts(Channel userChannel, Document d, String userName, ref int count, int max,
                                          bool fullTree)
        {
            ArrayList list = new ArrayList();
            ContentType ct = d.ContentType;
            if (ct.Alias.Equals(userChannel.DocumentTypeAlias) &
                (count < max))
            {
                list.Add(d);
                count = count + 1;
            }

            if (d.Children != null && d.Children.Length > 0 && fullTree)
                foreach (Document child in d.Children)
                    if (count < max)
                        list.AddRange(findBlogPosts(userChannel, child, userName, ref count, max, true));
            return list;
        }

        private string removeLeftUrl(string text)
        {
            return
                text.Replace(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority), "");
        }

        #endregion

        class DocumentDateComparer : IComparer
        {
            int IComparer.Compare(Object x, Object y)
            {
                if (((Document)x).CreateDateTime > ((Document)y).CreateDateTime)
                    return -1;
                else
                    return 0;
            }
        }

        class DocumentSortOrderComparer : IComparer
        {
            int IComparer.Compare(Object x, Object y)
            {
                if (((CMSNode)x).sortOrder > ((CMSNode)y).sortOrder)
                    return -1;
                else
                    return 0;
            }
        }
    }
}
